package com.net;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.util.Log;
import android.widget.ImageView;

import com.constant.Constant;

public class AsyncBmpLoader
{
	private ThreadPoolExecutor mThreadPool = null;//线程池
	private HashMap<String, SoftReference<Bitmap>> mCacheMap = null;
	private static AsyncBmpLoader _instance;
	
	private AsyncBmpLoader()
	{
		this.mThreadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(6);
		
		/*SoftReference软引用
		 * 如果一个对象只具有软引用，则如果内存够则不会回收他，但是内存不够时，就会回收。
		 * 软引用可用来实现内存敏感的高速缓存。该软引用的存在不妨碍垃圾收集线程对该Java对象的回收
		 * */
		this.mCacheMap = new HashMap<String, SoftReference<Bitmap>>();
	}
	
	
	/*synchronized代表这个方法加锁，
	 * 如果一个线程调用这个方法，如果其他线程正在使用，
	 * 则需要等其他线程使用完了过后，本线程才开始执行。
	 * */
	public synchronized static AsyncBmpLoader getInstance()
	{
		if(_instance == null)
		{
			_instance = new AsyncBmpLoader();
		}
		
		return _instance;
	}
	
	/**
	 * 此处加载图片，即使图片已在缓存中，同样开启一个任务加载图片，主要原因在于更好的防止多次调用时的数据不同步
	 */
	public void loadBitmap(final String url, final ImageView imageView,  final long viewId, final ImageCallback callback)
	{
		final Handler handler = new Handler() 
		{
			/*覆盖Handler的handleMessage的方法，操作传过来的数据，装载图片*/
			public void handleMessage(android.os.Message msg) 
			{
				callback.loadImage(imageView, viewId, (Bitmap) msg.obj);
				/*装载图片，参数有一个图片框，图片框ID,图片框要装载的位图文件*/
			};
		};
		
		mThreadPool.execute(new Runnable()
		{
			public void run()
			{
				// 设置为后台线程，当主线程退出时，所有的后台线程都会退出
				Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
				//先看本地是否有该图片，如果有就直接加载，如果没有，就从该网页中装载进来
				Bitmap bitmap = null;
				bitmap = loadBitmapForCache(url);
				if(bitmap != null)
				{
					Message msg = handler.obtainMessage(0, bitmap);
					handler.sendMessage(msg);
					return;
				}
				
				// 通过网络请求加载图片
				
				try
				{
					/*Constant.SET_CONNECTION_TIMEOUT=8000是指网络连接的最大时间*/
					bitmap = loadBitmapForNetwork(url, Constant.SET_CONNECTION_TIMEOUT);
					Message msg = handler.obtainMessage(0, bitmap);
					handler.sendMessage(msg);
					if(bitmap != null)
					{
						String cacheImage = Constant.BASE_FILE_PATH + url.substring(7).replaceAll("/", "_");
						//保存的该图片的名字“/mnt/sdcard/xihua+        ";
						mCacheMap.put(cacheImage, new SoftReference<Bitmap>(bitmap));
						/*把该位图文件存入HashMap中*/
						
						// 保存图片数据到文件中
						saveBitmap(url, bitmap);
					}
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
				
			}
		});
	}
	
	/**
	 * 可以自定义连接时间，用于加载向首页的那种不允许失败的图片，在此处把connectionTime设置大一些
	 */
	//上个装载位图的函数，由于连接时间固定是8000，如果由于首页那种不允许失败的图片，就可设置连接时间。
		//但是这里根本没有用到longTime
	public void loadBitmap(final String url, final ImageView imageView,  final long viewId,
			final boolean longTime, final ImageCallback callback)
	{
		final Handler handler = new Handler() 
		{
			public void handleMessage(android.os.Message msg) 
			{
				callback.loadImage(imageView, viewId, (Bitmap) msg.obj);
			};
		};
		
		mThreadPool.execute(new Runnable()
		{
			public void run()
			{
				// 设置为后台线程，当主线程退出时，所有的后台线程都会退出
				Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
				Bitmap bitmap = null;
				bitmap = loadBitmapForCache(url);
				if(bitmap != null)
				{
					Message msg = handler.obtainMessage(0, bitmap);
					handler.sendMessage(msg);
					return;
				}
				
				// 通过网络请求加载图片
				
				try
				{
					bitmap = loadBitmapForNetwork(url, Constant.SET_CONNECTION_TIMEOUT);//为改变SET_CONNECTION_TIMEOUT的值
					Message msg = handler.obtainMessage(0, bitmap);
					handler.sendMessage(msg);
					if(bitmap != null)
					{
						String cacheImage = Constant.BASE_FILE_PATH + url.substring(7).replaceAll("/", "_");
						mCacheMap.put(cacheImage, new SoftReference<Bitmap>(bitmap));
						
						// 保存图片数据到文件中
						saveBitmap(url, bitmap);
					}
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
				
			}
		});
	}
	
	/*本函数作用：
	 * 从网络中加载位图文件*/
	private Bitmap loadBitmapForNetwork(final String url, int connectionTime) throws Exception
	{
		Bitmap bitmap = null;
		HttpParams params = new BasicHttpParams();
		HttpConnectionParams.setConnectionTimeout(params, connectionTime);//代表一种Htpp协议参数，用于适用Http连接
		HttpClient httpClient = new DefaultHttpClient(params);//创建一个HttpClient对象
		//对于比较复杂的联网操作，需要使用HttpClient。
		HttpGet get = new HttpGet(url);
		//HttpGet类代表发送GET请求
		
		HttpResponse response = httpClient.execute(get);//通过HttpClient对象执行get操作得到HttpResponse对象
		//HttpResponse类代表处理相应的对象
		if(response.getStatusLine().getStatusCode() == 200)//本次http请求成功
		{
			InputStream bitmapStream = null;//输入流bitmapStream
			bitmapStream = response.getEntity().getContent();//通过getEntity()方法获得服务器相应内容的HttpEntity对象
			//HttpEntiny对象调用getContent()方法得到输入流
			
			/**
			 * 解决不同图片格式数据问题
			 */
			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();//字节数组输出流
			byte [] buffer =  new byte[1024]; //一个字节数组容器
			int ch;//读取的长度
			while((ch = bitmapStream.read(buffer)) != -1)
			{
				outputStream.write(buffer, 0, ch);//输出流写入(字节数组，该字节数组的第0个字节，到第ch个字节)
			}
			
			bitmapStream.close();//输入流文件关闭
			byte [] bitmapByte = outputStream.toByteArray();//把该字节数组输出流转换为字节数组
			outputStream.close();//字节输出流关闭
			
			bitmap = BitmapFactory.decodeByteArray(bitmapByte, 0, bitmapByte.length);
			//将该字节数组转换为位图，参数含义(字节数组名，从第0个字节开始，到字节数组的长度的字节结束);
			if(bitmap == null)
			{
				throw new Exception("二进制数据转换图片失败");
			}
		}
		else
		{
			throw new Exception("图片数据加载失败");
		}
		
		return bitmap;
	} // loadBitmapForNetwork
	
	/*本函数作用:
	 * 通过缓存的位图文件，即本地的文件，装载图片*/
	private Bitmap loadBitmapForCache(final String url)
	{
		if(url == null || url.length() == 0)
			return null;
		
		String cacheImage = Constant.BASE_FILE_PATH + url.substring(7).replaceAll("/", "_");
		Bitmap bitmap = BitmapFactory.decodeFile(cacheImage);
		//从这个目录下装载位图文件
		//BitmapFactory是一个工具类，用于从不同的数据源来解析创建Bitmap对象
		//decodeFile(String pathName)从给定的路径所指定的文件中解析创建一个位图文件
		//decodeFileDescriptor(FileDescriptor id)从FileDescriptor对应的文件中解析创建一个位图对象
	    //decodeResourse(Resourses res,int id)根据给定的资源id，从指定的资源中解析创建一个位图对象
		//decodeStream(InoutStream is)从指定的输入流中解析创建位图对象
		cacheImage = null;
		if(bitmap != null)
			return bitmap;
		
		return null;
		
	} // loadBitmapForCache
	
	/*本函数作用：
	 * 保存位图到本地文件*/
	private void saveBitmap(final String url, final Bitmap bitmap)
	{
		String cachePath = Constant.BASE_FILE_PATH + url.substring(7).replaceAll("/", "_");//生成该文件存储的地址
		File bitmapFile = new File(cachePath);//新建一个文件
		cachePath = null;
		
		try
		{
			if(!bitmapFile.exists()) {//如果该位图文件不存在
				//文件输出流fops
				FileOutputStream fops = null;
				//该文件类创建一个新的文件
				bitmapFile.createNewFile();
				//使用FileOutPutSteam(File file)方法，使用File对象作为目的地创建
				//FileOutPutSteam对象
				fops = new FileOutputStream(bitmapFile);
				bitmap.compress(Bitmap.CompressFormat.PNG, 80, fops);
				/*compress(Bitmap.CompressFormat format,int quality,OutputStram stream)
				功能：将Bitmap对象压缩为指定格式并保存在指定的文件输出流中，
				其中format参数可以为：PNG,JPEG,WEBP.*/
				
				fops.flush();
				//刷新流，使用此方法可是是输出流里面的数据写入文件中。
				fops.close();
			}
		}
		catch(Exception e)
		{
			Log.e("tab", "保存图片文件失败");
			e.printStackTrace();
		}
		
		bitmapFile = null;
	} // saveBitmap
	
	public synchronized void clearTask()
	{
		BlockingQueue<Runnable> queue = mThreadPool.getQueue();//清空线程池所使用的缓冲队列
		queue.clear();
	}
	
}
